package com.david.auth.service.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.david.auth.dao.AuthClientMapper;
import com.david.auth.service.IAuthClientService;
import com.david.auth.util.CryptoUtils;
import com.david.auth.util.StringUtils;
import com.david.exception.BusinessRuntimeException;
import com.david.auth.jwt.JwtToken;
import com.david.auth.pojo.constants.AuthBiz;
import com.david.auth.pojo.constants.Constants;
import com.david.auth.pojo.entity.AuthClientEntity;
import com.david.auth.pojo.query.SignQuery;
import com.david.auth.pojo.query.TokenQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import static com.david.auth.pojo.constants.Constants.Redis.TIME_OUT;

/**
 * <p>
 * 授权客户端管理 Service服务实现类
 * </p>
 *
 * @author David
 * @Date 2022-04-18 15:26:46
 */
@Service
public class AuthClientServiceImpl extends ServiceImpl<AuthClientMapper, AuthClientEntity> implements IAuthClientService {

    @Resource
    private AuthClientMapper authClientMapper;

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 验证客户端sign
     *
     * @param tokenQuery
     * @param authClient
     * @return
     */
    private boolean validateClient(SignQuery tokenQuery, AuthClientEntity authClient) {
        return Optional.ofNullable(authClient).map(t -> {
            String strJoin = StringUtils.getStringJoin(authClient.getApp_key(), authClient.getApp_secret());

            if (!CryptoUtils.encryptSha256Hex(strJoin).equals(tokenQuery.getSign())) {
                throw new BusinessRuntimeException(AuthBiz.FAIL_AUTH);
            }
            return true;
        }).orElseThrow(() -> new BusinessRuntimeException(AuthBiz.FAIL_APPLY));

    }

    @Override
    @Transactional(readOnly = true)
    public AuthClientEntity getAuthClient(String appkey) {
        return authClientMapper.selectOne(new QueryWrapper<AuthClientEntity>().eq("app_key", appkey));
    }

    @Override
    @Transactional(readOnly = true)
    public boolean verifyToken(TokenQuery tokenQuery) {
        String token = (String) redisTemplate.opsForValue().get(Constants.Redis.AUTH_KEY + tokenQuery.getAppKey());
        if (Objects.isNull(token)) {
            return false;
        }
        if (!tokenQuery.getToken().equals(token)) {
            return false;
        }
        return true;
    }

    @Override
    @Transactional(readOnly = true)
    public String getToken(SignQuery tokenQuery) {
        AuthClientEntity authClient = getAuthClient(tokenQuery.getAppKey());

        //验证sign
        validateClient(tokenQuery, authClient);

        //生成token
        String token = (String) redisTemplate.opsForValue().get(Constants.Redis.AUTH_KEY + tokenQuery.getAppKey());
        return Optional.ofNullable(token).orElseGet(() -> {
            String jwt = createJwt(authClient);
            redisTemplate.opsForValue().set(Constants.Redis.AUTH_KEY + tokenQuery.getAppKey(), jwt, TIME_OUT, TimeUnit.HOURS);
            return jwt;
        });
    }

    /**
     * 创建jwt令牌
     *
     * @param authClient
     * @return
     */
    private String createJwt(AuthClientEntity authClient) {
        Map<String, String> map = new HashMap<>(2);
        map.put("appKey", authClient.getApp_key());
        map.put("typ", "JWT");

        String jwt = null;
        try {
            jwt = JwtToken.createToken(map, authClient.getApp_secret());
        } catch (Exception e) {
            log.error("create jwt token failed", e);
        }
        return jwt;
    }
}
